                                                                             /*
  _  _                                                                        *
  _||_                                                                        *
   ||                                                 folkert@feedface.com    *
                                                                             */



#import "ActionControl.h"
#import "NetHackViewController.h"



NSString* OCTANTNAME[9]= {
    @"EAST",
    @"NORTHEAST",
    @"NORTH",
    @"NORTHWEST",
    @"WEST",
    @"SOUTHWEST",
    @"SOUTH",
    @"SOUTHEAST",
    @"CENTER"
};

#define PI (3.1415926535897932384626433832795028841971693993751058209749445920)

#define SEGMENT_ARC (2.0*PI/8.0)
#define RADIUS_CENTER (50.0)
#define RADIUS_LABEL  (85.0)
#define RADIUS_OUTER  (120.0)
#define RADIUS_RING   (150.0)

#define TITLE_FONT_NAME "Verdana"
#define TITLE_FONT_SIZE (16)


CGFloat
distanceToCenter(CGPoint p)
{
    return sqrtf( p.x*p.x + p.y*p.y );
}

CGFloat
angleToCenterWithDistance(CGPoint p, CGFloat distance)
{
    if (p.y < 0.0)
        return 2.0*PI - acosf( p.x / distance );
    return acosf( p.x / distance );
}

CGFloat 
angleToCenter(CGPoint p)
{
    return angleToCenterWithDistance(p, distanceToCenter(p));
}

int
octantForAngle(CGFloat a)
{
    int ret= 0;
    CGFloat b= a + 0.5 * SEGMENT_ARC;
    CGFloat c= b/(2.0*PI);
    ret= ((int)(c * 8.0))%8;
    return ret;
}

char
nethackMovementCharForOcant(int octant)
{
    switch (octant) {
        case CENTER:    return ' ';
        case EAST:      return 'l';
        case NORTHEAST: return 'u';
        case NORTH:     return 'k';
        case NORTHWEST: return 'y';
        case WEST:      return 'h';
        case SOUTHWEST: return 'b';
        case SOUTH:     return 'j';
        case SOUTHEAST: return 'n';
        default:        return ' ';
    }
}

#pragma mark -

@implementation ActionControl

@synthesize coordinateTransform;
@synthesize descriptionLabel;
@synthesize controller;

//work with a coordinate system with (0.0) in center and positive y axis pointing 'up'
- (void)
resetCoordinateTransform
{
    CGSize size= self.bounds.size;
    coordinateTransform= CGAffineTransformIdentity;
    coordinateTransform= CGAffineTransformScale(coordinateTransform, 1.0, -1.0);
    coordinateTransform= CGAffineTransformTranslate(coordinateTransform, -1.0 * size.width/2.0, -1.0 * size.height/2.0);

    
}



- (id)
initWithFrame: (CGRect) frame andDictionary: (NSDictionary*) dictionary
{
    if (self= [super initWithFrame: frame]) {
    
        [self resetCoordinateTransform];
        self.userInteractionEnabled= TRUE;
        self.backgroundColor= [UIColor clearColor];
        
        
        for (Octant i= EAST; i<=SOUTHEAST; i++) {
            segment[i]= nil;
            segmentHotness[i]= 1.0;
        }
 
        activeSegment= CENTER;
        
        NSArray *dicts= (NSArray*) [dictionary objectForKey: @"subsegments"];
        for (NSDictionary *subdict in dicts) {
            Segment *s= [[Segment alloc] initWithDictionary: subdict];
            segment[s.octant]= s;
        }

        for (Octant i= 0; i<8; i++)
            if (segment[i] == nil)
                NSLog(@"WARNING: no segment for octant %@",OCTANTNAME[i]);
		
		usingQuickCommandSelection = YES;
    }
    return self;
}




- (void)
drawRect: (CGRect) rect
{


    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextTranslateCTM(context,self.bounds.size.width/2.0,self.bounds.size.height/2.0);
    CGContextScaleCTM(context,1.0,-1.0);


    if (showSegments) {
    
        for (Octant i= EAST; i<=SOUTHEAST; i++) {
            Segment *seg;
            if (showSubSegments) {
                seg= [segment[selectedSegment] subSegmentAtOctant: i];
            }
            else {
                seg= segment[i];
            }
                
            if (!seg)
                continue;
        
            //draw segment 
            CGFloat a= ((CGFloat) i) * SEGMENT_ARC - 0.5 * SEGMENT_ARC;
            CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
            CGContextMoveToPoint(context, 0.0, 0.0);
            CGContextAddArc(context, 0.0, 0.0, RADIUS_CENTER, a ,a + SEGMENT_ARC , 0);
            CGContextAddArc(context, 0.0, 0.0, RADIUS_OUTER, a + SEGMENT_ARC ,a, 1);
            CGContextFillPath(context);

            //draw segment title
            const char *txt= [seg.title UTF8String];
            CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, segmentHotness[i]);
            CGContextSelectFont(context, TITLE_FONT_NAME, TITLE_FONT_SIZE, kCGEncodingMacRoman);
            CGContextShowTextAtPoint(context, seg.titleCoordinates.x, seg.titleCoordinates.y, txt, strlen(txt));
 
        }

        CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, ringHotness);
        CGContextMoveToPoint(context, 0.0, 0.0);
        CGContextAddArc(context, 0.0, 0.0, RADIUS_OUTER, 0.0 , 2.0*PI , 0);
        CGContextAddArc(context, 0.0, 0.0, RADIUS_RING, 0.0, 2.0*PI , 1);
        CGContextFillPath(context);
    
        return;
    }



    
    if (activeSegment == CENTER)
        return;
    
    //did not begin in center, only show one movement segment
    CGFloat a= ((CGFloat) activeSegment) * SEGMENT_ARC - 0.5 * SEGMENT_ARC;
    CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
    CGContextMoveToPoint(context, 0.0, 0.0);
    CGContextAddArc(context, 0.0, 0.0, RADIUS_CENTER, a ,a + SEGMENT_ARC , 0);
    CGContextAddArc(context, 0.0, 0.0, RADIUS_OUTER, a + SEGMENT_ARC ,a, 1);
    CGContextFillPath(context);
    return;

    
}

#pragma mark Commands

- (void)
beginCommandAtPoint:(CGPoint)p
{
	CGFloat d= distanceToCenter(p);
    CGFloat a= angleToCenter(p);
    Octant o= octantForAngle(a);
    
    
    
    if ( d > RADIUS_CENTER) {
    
        showSegments= FALSE;
        activeSegment= o;
        descriptionLabel.text= OCTANTNAME[activeSegment];
    }
    
    else { //touch in center
    
        showSegments= TRUE;
    
    
    }


    [self setNeedsDisplay];
}

- (void)
continueCommandAtPoint:(CGPoint)p
{
    CGFloat d= distanceToCenter(p);
    CGFloat a= angleToCenter(p);
    Octant o= octantForAngle(a);
    
    if (showSegments) {
    
 
        if ( d < RADIUS_CENTER && showSubSegments) {
        
            showSubSegments= FALSE;
            selectedSegment= CENTER;
            activeSegment= CENTER;
            descriptionLabel.text= @"";
            for (Octant i= EAST; i<=SOUTHEAST; i++)
                segmentHotness[i]= 1.0;
            [self setNeedsDisplay];
            return;
        }
   
        if (d < RADIUS_CENTER) { //touch back in center
        
            descriptionLabel.text= @"";
            for (Octant i= EAST; i<=SOUTHEAST; i++)
                segmentHotness[i]= 1.0;
    
            [self setNeedsDisplay];
            return; 
        }
    
    
        if (d < RADIUS_OUTER && !showSubSegments) {
            activeSegment= o;
            CGFloat h= (d-RADIUS_CENTER) / (RADIUS_OUTER - RADIUS_CENTER);
            ringHotness= h;
            for (Octant i= EAST; i<=SOUTHEAST; i++) {
                segmentHotness[i]= 1.0 - h;
     
            }
            segmentHotness[activeSegment]= 1.0;
            descriptionLabel.text= segment[activeSegment].description;
            [self setNeedsDisplay];
            return; 
        }
        
        if ( d > RADIUS_OUTER && !showSubSegments) { //touched moved to ring first time
        
            selectedSegment= o;
            activeSegment= o;
            showSubSegments= TRUE;
            CGFloat h= (d-RADIUS_OUTER) / (RADIUS_RING - RADIUS_OUTER);
            for (Octant i= EAST; i<=SOUTHEAST; i++)
                segmentHotness[i]= h;
            segmentHotness[activeSegment]= 1.0;
            descriptionLabel.text= segment[activeSegment].description;
            [self setNeedsDisplay];
            return;
        }
        
        if ( d > RADIUS_OUTER && showSubSegments) { //stayed outside
            activeSegment= o;
            CGFloat h= (d-RADIUS_OUTER) / (RADIUS_RING - RADIUS_OUTER);
            for (Octant i= EAST; i<=SOUTHEAST; i++)
                segmentHotness[i]= h;
            segmentHotness[activeSegment]= 1.0;
            descriptionLabel.text= [segment[selectedSegment] subSegmentAtOctant: o].description;
            [self setNeedsDisplay];
            return;
        
        }
        

        
        if ( d < RADIUS_OUTER && showSubSegments) {
            activeSegment= o;
            CGFloat h= (d-RADIUS_OUTER) / (RADIUS_RING - RADIUS_OUTER);
            for (Octant i= EAST; i<=SOUTHEAST; i++)
                segmentHotness[i]= h;
            segmentHotness[activeSegment]= 1.0;
            descriptionLabel.text= [segment[selectedSegment] subSegmentAtOctant: o].description;
            [self setNeedsDisplay];
            return;
        }
        
        
    
    } 
    
    else { //touch started outside of center
    
    
        if (d < RADIUS_CENTER && activeSegment == CENTER)  //touch stayed in center
            return;
        
        if (d < RADIUS_CENTER) { //touch moved inside of center
            
            descriptionLabel.text= @"";
            activeSegment= CENTER;
            [self setNeedsDisplay];
            return;
        
        }
        
        activeSegment= o;
        descriptionLabel.text= OCTANTNAME[activeSegment];
        [self setNeedsDisplay];
        return;
    
    }


}

- (void)
endCommandAtPoint:(CGPoint)p
{
    CGFloat d= distanceToCenter(p);
    CGFloat a= angleToCenter(p);
    Octant o= octantForAngle(a);

    
    if (!showSegments && d > RADIUS_CENTER) {
        [controller putCharacter: nethackMovementCharForOcant(activeSegment)];
    }
    
    else if (!showSubSegments && d > RADIUS_CENTER) {
        [controller putCharacter: segment[o].character];
    }
    
    else if (showSubSegments && d > RADIUS_CENTER) {
        [controller putCharacter: [segment[selectedSegment] subSegmentAtOctant: activeSegment].character ];
    }
    
    
    for (Octant i= EAST; i<=SOUTHEAST; i++)
        segmentHotness[i]= 1.0;
    activeSegment= CENTER;
    selectedSegment= CENTER;
    showSegments= FALSE;
    descriptionLabel.text= @"";
    showSubSegments= FALSE;
    ringHotness= 0.0;
    [self setNeedsDisplay];
}

#pragma mark Touches


- (void)
touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
    UITouch *touch= (UITouch*) [[touches allObjects] objectAtIndex: 0];
    CGPoint p= CGPointApplyAffineTransform([touch locationInView: self], coordinateTransform);
	
	if (usingQuickCommandSelection) {
		[self beginCommandAtPoint:p];
	} else {
		[self continueCommandAtPoint:p];
	}
}

- (void)
touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
{
	UITouch *touch= (UITouch*) [[touches allObjects] objectAtIndex: 0];
    CGPoint p= CGPointApplyAffineTransform([touch locationInView: self], coordinateTransform);	

	if (usingQuickCommandSelection) {
		[self continueCommandAtPoint:p];
	}
}


- (void)
touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event {
	UITouch *touch= (UITouch*) [[touches allObjects] objectAtIndex: 0];
    CGPoint p= CGPointApplyAffineTransform([touch locationInView: self], coordinateTransform);
	
	if (usingQuickCommandSelection) {
		[self endCommandAtPoint:p];
		
		CGFloat d= distanceToCenter(p);
		if (d < RADIUS_CENTER) {
			usingQuickCommandSelection = NO;
			[self beginCommandAtPoint:p];
		}
	} else if (touch.tapCount >= 2) {
		[self endCommandAtPoint:p];
		usingQuickCommandSelection = YES;
	}
}





@end


@implementation Segment

@synthesize octant;
@synthesize title;
@synthesize description;
@synthesize character;
@synthesize titleCoordinates;

- (id)
initWithDictionary: (NSDictionary*) dictionary
{
    if (self= [super init]) {
        for (int i= 0; i<8; i++)
            subsegment[i]= nil;
        description= [dictionary objectForKey: @"description"];
        title= [dictionary objectForKey: @"title"];
        NSNumber *o= (NSNumber*) [dictionary objectForKey: @"octant"];    
        octant= [o intValue];
        NSString *c= (NSString*) [dictionary objectForKey: @"character"];
        character= *[c UTF8String];
        
        if ( [(NSNumber*) [dictionary objectForKey: @"meta"] boolValue] ) {
            character= character | 0x80;
        }
        else if ( [(NSNumber*) [dictionary objectForKey: @"ctrl"] boolValue] ) {
            character= character & 0x1f;
        }


        //init coordinates for titles
        CGFloat a= ((CGFloat)octant) * SEGMENT_ARC - 0.5*SEGMENT_ARC;
        CGSize size= [title sizeWithFont: [UIFont fontWithName: [NSString stringWithUTF8String: TITLE_FONT_NAME] size: TITLE_FONT_SIZE]];
        titleCoordinates.x= (RADIUS_LABEL) * cosf(a + 0.5 * SEGMENT_ARC) - (size.width/2.0);
        titleCoordinates.y= (RADIUS_LABEL) * sinf(a + 0.5 * SEGMENT_ARC) - (size.height/4.0);
       
//        NSLog(@"created segment %@ at %@",title,OCTANTNAME[octant]);
        
        NSArray *dicts= (NSArray*) [dictionary objectForKey: @"subsegments"];
        for (NSDictionary *subdict in dicts) {
            Segment *s= [[Segment alloc] initWithDictionary: subdict];
            subsegment[s.octant]= s;
//            NSLog(@"  created subsegment %@ at %@",s.title,OCTANTNAME[s.octant]);
        }
        
        
        
    }
    return self;
}



- (Segment*) 
subSegmentAtOctant: (Octant) o
{
    if (o == CENTER)
        return nil;
    if (octant == o)
        return self;
    return subsegment[o];
}


@end


